﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.InteropServices;

namespace ProgShortcut
{
    public partial class frmMenu : Form
    {
        private Shortcut info;
        private frmIcon icons;
        public frmMenu()
        {
            InitializeComponent();
            info = Shortcut.Demarrer(Application.StartupPath);
            info.Change += info_Change;
            info_Change(null, null);
            Hide();
            this.ShowInTaskbar = false;
        }
        private void info_Change(object sender, ChangeEventArgs e)
        {
            txtCatName.Text = "";
            txtFileName.Text = "";
            txtPath.Text = "";
            cboCat.Items.Clear();
            cboCat.Items.AddRange(info.GetCategories().ToArray());
            cboCat.SelectedItem = null;
            cboCat.Text = "";
            lbxFile.Items.Clear();
            btnSupCat.Visible = false;
            notiProg.ContextMenuStrip = info.GetContextMenu();
        }
        private void btnParcour_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Multiselect = false;
            ofd.InitialDirectory = "C:\\";
            ofd.Filter = "EXE Files(*.exe)|*.exe";
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                txtPath.Text = ofd.FileName;
        }
        private void txtPath_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }
        private void txtPath_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                String filePath = ((string[])(e.Data.GetData(DataFormats.FileDrop)))[0];
                if (filePath.EndsWith(".exe"))
                    txtPath.Text = filePath;
            }
        }
        private void btnAddCat_Click(object sender, EventArgs e)
        {
            if (txtCatName.Text != "")
                info.AddCategorie(txtCatName.Text);
        }
        private void cboCat_SelectedValueChanged(object sender, EventArgs e)
        {
            if (cboCat.SelectedItem != null)
            {
                lbxFile.Items.Clear();
                if (((Categorie)cboCat.SelectedItem).Count > 0)
                {
                    lbxFile.Items.AddRange(((Categorie)cboCat.SelectedItem).getFile().ToArray());
                    lbxFile.SelectedValue = null;
                    btnSupCat.Visible = false;
                }
                else
                    btnSupCat.Visible = true;
            }
        }
        private void btnSupCat_Click(object sender, EventArgs e)
        {
            info.DelCategorie(cboCat.Text);
        }
        private void btnSupFile_Click(object sender, EventArgs e)
        {
            if (lbxFile.SelectedItem != null)
                info.DelFile(cboCat.Text, lbxFile.SelectedItem.ToString());
        }
        private void btnAddFile_Click(object sender, EventArgs e)
        {
            if (cboCat.SelectedItem != null && txtPath.Text != "" && txtFileName.Text != "")
                info.AddFile(cboCat.Text, new Fichier(txtFileName.Text, txtPath.Text));
            else if (cboCat.SelectedItem != null && txtFileName.Text != "" && cboCat.Text == "Fonction de système")
                info.AddFile(cboCat.Text, new Fichier(txtFileName.Text, ""));
        }
        private void notiProg_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                if (!frmIcon.Showing)
                {
                    icons = new frmIcon(info);
                    icons.Show();
                }
                else
                    icons.Activate();
            }
        }
        private void notiProg_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            Show();
            WindowState = FormWindowState.Normal;
        }

        private void frmMenu_Resize(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
                Hide();
        }
    }

    public class Shortcut
    {
        public delegate void ChangeEventHandler(object sender, ChangeEventArgs e);
        public event ChangeEventHandler Change;

        private Categories categories;
        private String chemin;

        private Shortcut(String chemin)
        {
            this.chemin = chemin;
            categories = LireEcrire.Deserialise<Categories>(chemin + "\\apl.txt");
            if (categories == null)
            {
                categories = new Categories();
                categories.Add(SystemeFonction.Demarrer(chemin));
                LireEcrire.Serialise(chemin + "\\apl.txt", categories);
            }
            categories.Change += Shortcut_Change;
        }
        public static Shortcut Demarrer(String chemin)
        {
            return new Shortcut(chemin);
        }
        private void Shortcut_Change(object sender, ChangeEventArgs e)
        {
            try { Change(this, new ChangeEventArgs()); }
            catch { }
            LireEcrire.Serialise(chemin + "\\apl.txt", categories);
        }
        public void AddCategorie(String nom)
        {
            if (categories.FindIndex(nom) == -1) { categories.Add(new Categorie(nom)); }
        }
        public void DelCategorie(String nom) { categories.Remove(nom); }
        public void AddFile(String nomCat, Fichier f)
        {
            int index = categories.FindIndex(nomCat);
            if (index > -1) { categories[categories.FindIndex(nomCat)].Add(f); }
        }
        public void DelFile(String nomCat, String nomFile)
        {
            int index = categories.FindIndex(nomCat);
            if (index > -1) { categories[categories.FindIndex(nomCat)].Remove(nomFile); }
        }
        public List<Categorie> GetCategories() { return categories.GetCategories(); }
        public ContextMenuStrip GetContextMenu()
        {
            ContextMenuStrip cms = categories.GetContextMenu();
            ToolStripMenuItem ts = new ToolStripMenuItem("Fermer");
            ts.Click += ts_Click;
            cms.Items.Add(ts);
            return cms;
        }
        private void ts_Click(object sender, EventArgs e) { Application.Exit(); }
    }

    [Serializable]
    public class Categories
    {
        public delegate void ChangeEventHandler(object sender, ChangeEventArgs e);
        [field: NonSerialized]
        public event ChangeEventHandler Change;
        
        private List<Categorie> categories;

        public Categories()
        {
            categories = new List<Categorie>();
        }
        private void Categories_Change(object sender, ChangeEventArgs e)
        {
            try { Change(this, new ChangeEventArgs()); }
            catch { }
        }
        public void Add(Categorie c)
        {
            c.Change += Categories_Change;
            for (int a = 0; a < categories.Count; a++)
            {
                if (categories[a].Nom.CompareTo(c.Nom) > 0)
                {
                    categories.Insert(a, c);
                    Categories_Change(this, new ChangeEventArgs());
                    return;
                }
            }
            categories.Add(c);
            Categories_Change(this, new ChangeEventArgs());
        }
        public void Remove(String nom)
        {
            int index = FindIndex(nom);
            if (index > -1)
                RemoveAt(index);
        }
        public void RemoveAt(int index)
        {
            categories.RemoveAt(index);
            Categories_Change(this, new ChangeEventArgs());
        }
        public Categorie this[int index]
        {
            get { return categories[index]; }
            set { categories[index] = value; }
        }
        public int FindIndex(String nom)
        {
            int[] range = { 0, categories.Count - 1 };
            int index;
            while (range[1] - range[0] >= 0)
            {
                index = (range[1] - range[0]) / 2 + range[0];
                if (categories[index].Nom == nom)
                    return index;
                else
                    range = categories[index].Nom.CompareTo(nom) > 0 ? new int[] { range[0], index - 1 } : new int[] { index + 1, range[1] };
            }
            return -1;
        }
        public int Count { get { return categories.Count; } }
        public List<Categorie> GetCategories() { return categories; }
        public ContextMenuStrip GetContextMenu()
        {
            ContextMenuStrip cms = new ContextMenuStrip();
            foreach (Categorie c in categories)
                cms.Items.Add(c.GetToolStrip());
            return cms;
        }
    }

    [Serializable]
    public class Categorie
    {
        public delegate void ChangeEventHandler(object sender, ChangeEventArgs e);
        public event ChangeEventHandler Change;

        private List<Fichier> file;
        public String Nom { get; set; }
        public Categorie(String nom)
        {
            this.Nom = nom;
            file = new List<Fichier>();
        }
        public void Add(Fichier f)
        {
            for (int a = 0; a < file.Count; a++)
            {
                if (file[a].Nom.CompareTo(f.Nom) > 0)
                {
                    file.Insert(a, f);
                    try { Change(this, new ChangeEventArgs()); }
                    catch { return; }
                    return;
                }
            }
            file.Add(f);
            try { Change(this, new ChangeEventArgs()); }
            catch { }
        }
        public void Remove(String nom)
        {
            int index = FindIndex(nom);
            if (index > -1)
                RemoveAt(index);
        }
        public void RemoveAt(int index)
        {
            file.RemoveAt(index);
            try { Change(this, new ChangeEventArgs()); }
            catch { }
        }
        public Fichier this[int index]
        {
            get { return file[index]; }
            set { file[index] = value; }
        }
        public int FindIndex(String nom)
        {
            int[] range = { 0, file.Count - 1 };
            int index;
            while (range[1] - range[0] >= 0)
            {
                index = (range[1] - range[0]) / 2 + range[0];
                if (file[index].Nom == nom)
                {
                    return index;
                }
                else
                {
                    range = file[index].Nom.CompareTo(nom) > 0 ? new int[] { range[0], index - 1 } : new int[] { index + 1, range[1] };
                }
            }
            return -1;
        }
        public int Count { get { return file.Count; } }
        public override String ToString()
        {
            return Nom;
        }
        public ToolStripMenuItem GetToolStrip()
        {
            ToolStripMenuItem ts = new ToolStripMenuItem();
            ts.Text = Nom;
            foreach (Fichier p in file)
                ts.DropDownItems.Add(p.GetToolStrip());
            return ts;
        }
        public List<Fichier> getFile() { return file; }
    }

    [Serializable]
    public class SystemeFonction : Categorie
    {
        public new delegate void ChangeEventHandler(object sender, ChangeEventArgs e);
        public new event ChangeEventHandler Change;

        [DllImport("user32")]
        private static extern void LockWorkStation();
        private SystemeFonction(String folder)
            : base("Fonction de système")
        {
            base.Add(new Fichier("Arrêter", arreter, Image.FromFile(folder + @"\\icon\\Arreter.png")));
            base.Add(new Fichier("Hiberner", hiberner, Image.FromFile(folder + @"\\icon\\Hiberner.png")));
            base.Add(new Fichier("Redémarrer", redemarrer, Image.FromFile(folder + @"\\icon\\Redemarrer.png")));
            base.Add(new Fichier("Veille", veille, Image.FromFile(folder + @"\\icon\\Veille.png")));
            base.Add(new Fichier("Vérrouiller", verrouiller, Image.FromFile(folder + @"\\icon\\Verrouiller.png")));
        }
        public static SystemeFonction Demarrer(String folder)
        {
            return new SystemeFonction(folder);
        }
        public new void Add(Fichier f)
        {
            int index = base.FindIndex(f.Nom + "[désactivé]");
            if (index > -1)
            {
                base[index].Nom = f.Nom;
                try { Change(this, new ChangeEventArgs()); }
                catch { }
            }
        }
        public new void Remove(String nom)
        {
            int index = base.FindIndex(nom);
            if (index > -1)
            {
                RemoveAt(index);
            }
        }
        public new void RemoveAt(int index)
        {
            base[index].Nom += "[désactivé]";
            try { Change(this, new ChangeEventArgs()); }
            catch { }
        }
        private void arreter()
        {
            Process.Start("shutdown", "/s /t 0");
        }
        private void redemarrer()
        {
            Process.Start("shutdown", "/r /t 0");
        }
        private void verrouiller()
        {
            LockWorkStation();
        }
        private void hiberner()
        {
            Application.SetSuspendState(PowerState.Hibernate, true, true);
        }
        private void veille()
        {
            Application.SetSuspendState(PowerState.Suspend, true, true);
        }
    }

    [Serializable]
    public class Fichier
    {
        public String Nom { get; set; }
        public Image Icon { get; set; }
        private Action action;
        private String chemin;
        public Fichier(String nom, String chemin)
        {
            this.Nom = nom;
            this.chemin = chemin;
            Icon = GetFileIcon(chemin, IconSize.Large, false).ToBitmap();
            action = new Action(createAction);
        }
        public Fichier(String nom, String chemin, Image icon)
        {
            this.Nom = nom;
            this.chemin = chemin;
            this.Icon = icon;
            action = new Action(createAction);
        }
        public Fichier(String nom, Action action, Image icon)
        {
            this.Nom = nom;
            this.chemin = "";
            this.Icon = icon;
            this.action = action;
        }
        public override String ToString()
        {
            return Nom;
        }
        public ToolStripMenuItem GetToolStrip()
        {
            ToolStripMenuItem ts = new ToolStripMenuItem(Nom);
            ts.Click += new EventHandler(ts_Click);
            return ts;
        }
        public void ts_Click(object sender, EventArgs e)
        {
            Action();
        }
        public void Action()
        {
            action.Invoke();
        }
        private void createAction()
        {
            Process myProcess = new Process();
            myProcess.StartInfo.UseShellExecute = false;
            myProcess.StartInfo.FileName = chemin;
            myProcess.StartInfo.CreateNoWindow = true;
            myProcess.Start();
        }
        private enum IconSize
        {
            /// <summary>
            /// Specify large icon - 32 pixels by 32 pixels.
            /// </summary>
            Large = 0,
            /// <summary>
            /// Specify small icon - 16 pixels by 16 pixels.
            /// </summary>
            Small = 1
        }
        private System.Drawing.Icon GetFileIcon(string name, IconSize size, bool linkOverlay)
        {
            Shell32.SHFILEINFO shfi = new Shell32.SHFILEINFO();
            uint flags = Shell32.SHGFI_ICON | Shell32.SHGFI_USEFILEATTRIBUTES;

            if (true == linkOverlay) flags += Shell32.SHGFI_LINKOVERLAY;

            /* Check the size specified for return. */
            if (IconSize.Small == size)
            {
                flags += Shell32.SHGFI_SMALLICON;
            }
            else
            {
                flags += Shell32.SHGFI_LARGEICON;
            }

            Shell32.SHGetFileInfo(name,
                Shell32.FILE_ATTRIBUTE_NORMAL,
                ref shfi,
                (uint)System.Runtime.InteropServices.Marshal.SizeOf(shfi),
                flags);

            // Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly
            System.Drawing.Icon icon = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shfi.hIcon).Clone();
            User32.DestroyIcon(shfi.hIcon);		// Cleanup
            return icon;
        }
    }
    public class LireEcrire
    {
        public static void Serialise<T>(String chemin, T s)
        {
            Stream TestFileStream = File.Create(chemin);
            BinaryFormatter serializer = new BinaryFormatter();
            serializer.Serialize(TestFileStream, s);
            TestFileStream.Close();
        }
        public static T Deserialise<T>(String chemin)
        {
            if (!File.Exists(chemin))
            {
                FileStream fs = File.Create(chemin);
                fs.Close();
            }
            if ((new FileInfo(chemin).Length) == 0)
                return default(T);
            Stream TestFileStream = File.OpenRead(chemin);
            BinaryFormatter deserializer = new BinaryFormatter();
            T s = (T)deserializer.Deserialize(TestFileStream);
            TestFileStream.Close();
            return s;
        }
    }
    [Serializable]
    public class ChangeEventArgs : System.EventArgs { }
    [Serializable]
    public class Shell32
    {
        public const int MAX_PATH = 256;
        [StructLayout(LayoutKind.Sequential)]
        public struct SHITEMID
        {
            public ushort cb;
            [MarshalAs(UnmanagedType.LPArray)]
            public byte[] abID;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ITEMIDLIST
        {
            public SHITEMID mkid;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BROWSEINFO
        {
            public IntPtr hwndOwner;
            public IntPtr pidlRoot;
            public IntPtr pszDisplayName;
            [MarshalAs(UnmanagedType.LPTStr)]
            public string lpszTitle;
            public uint ulFlags;
            public IntPtr lpfn;
            public int lParam;
            public IntPtr iImage;
        }

        // Browsing for directory.
        public const uint BIF_RETURNONLYFSDIRS = 0x0001;
        public const uint BIF_DONTGOBELOWDOMAIN = 0x0002;
        public const uint BIF_STATUSTEXT = 0x0004;
        public const uint BIF_RETURNFSANCESTORS = 0x0008;
        public const uint BIF_EDITBOX = 0x0010;
        public const uint BIF_VALIDATE = 0x0020;
        public const uint BIF_NEWDIALOGSTYLE = 0x0040;
        public const uint BIF_USENEWUI = (BIF_NEWDIALOGSTYLE | BIF_EDITBOX);
        public const uint BIF_BROWSEINCLUDEURLS = 0x0080;
        public const uint BIF_BROWSEFORCOMPUTER = 0x1000;
        public const uint BIF_BROWSEFORPRINTER = 0x2000;
        public const uint BIF_BROWSEINCLUDEFILES = 0x4000;
        public const uint BIF_SHAREABLE = 0x8000;

        [StructLayout(LayoutKind.Sequential)]
        public struct SHFILEINFO
        {
            public const int NAMESIZE = 80;
            public IntPtr hIcon;
            public int iIcon;
            public uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NAMESIZE)]
            public string szTypeName;
        };

        public const uint SHGFI_ICON = 0x000000100;     // get icon
        public const uint SHGFI_DISPLAYNAME = 0x000000200;     // get display name
        public const uint SHGFI_TYPENAME = 0x000000400;     // get type name
        public const uint SHGFI_ATTRIBUTES = 0x000000800;     // get attributes
        public const uint SHGFI_ICONLOCATION = 0x000001000;     // get icon location
        public const uint SHGFI_EXETYPE = 0x000002000;     // return exe type
        public const uint SHGFI_SYSICONINDEX = 0x000004000;     // get system icon index
        public const uint SHGFI_LINKOVERLAY = 0x000008000;     // put a link overlay on icon
        public const uint SHGFI_SELECTED = 0x000010000;     // show icon in selected state
        public const uint SHGFI_ATTR_SPECIFIED = 0x000020000;     // get only specified attributes
        public const uint SHGFI_LARGEICON = 0x000000000;     // get large icon
        public const uint SHGFI_SMALLICON = 0x000000001;     // get small icon
        public const uint SHGFI_OPENICON = 0x000000002;     // get open icon
        public const uint SHGFI_SHELLICONSIZE = 0x000000004;     // get shell size icon
        public const uint SHGFI_PIDL = 0x000000008;     // pszPath is a pidl
        public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;     // use passed dwFileAttribute
        public const uint SHGFI_ADDOVERLAYS = 0x000000020;     // apply the appropriate overlays
        public const uint SHGFI_OVERLAYINDEX = 0x000000040;     // Get the index of the overlay

        public const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
        public const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;

        [DllImport("Shell32.dll")]
        public static extern IntPtr SHGetFileInfo(
            string pszPath,
            uint dwFileAttributes,
            ref SHFILEINFO psfi,
            uint cbFileInfo,
            uint uFlags
            );
    }
    [Serializable]
    public class User32
    {
        /// <summary>
        /// Provides access to function required to delete handle. This method is used internally
        /// and is not required to be called separately.
        /// </summary>
        /// <param name="hIcon">Pointer to icon handle.</param>
        /// <returns>N/A</returns>
        [DllImport("User32.dll")]
        public static extern int DestroyIcon(IntPtr hIcon);
    }
}